/*
 * Copyright (c) 2022. China Mobile (SuZhou) Software Technology Co.,Ltd. All rights reserved.
 * Lakehouse is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package com.chinamobile.cmss.lakehouse.api.controller;

import static com.chinamobile.cmss.lakehouse.common.enums.Status.BATCH_DELETE_USER_INSTANCE_BY_IDS_ERROR;
import static com.chinamobile.cmss.lakehouse.common.enums.Status.GET_USER_INFO_ERROR;
import static com.chinamobile.cmss.lakehouse.common.enums.Status.QUERY_USER_LIST_PAGING_ERROR;
import static com.chinamobile.cmss.lakehouse.common.enums.Status.UPDATE_USER_ERROR;
import static com.chinamobile.cmss.lakehouse.common.enums.Status.USER_LIST_ERROR;
import static com.chinamobile.cmss.lakehouse.common.enums.Status.VERIFY_USERNAME_ERROR;

import com.chinamobile.cmss.lakehouse.api.service.UserService;
import com.chinamobile.cmss.lakehouse.common.Constants;
import com.chinamobile.cmss.lakehouse.common.annotation.AccessLogAnnotation;
import com.chinamobile.cmss.lakehouse.common.annotation.ApiException;
import com.chinamobile.cmss.lakehouse.common.enums.Status;
import com.chinamobile.cmss.lakehouse.common.utils.Result;
import com.chinamobile.cmss.lakehouse.dao.entity.UserEntity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

@Slf4j
@Api(value = "User Administration", protocols = "http")
@RestController
@RequestMapping("/users")
public class UserController extends BaseController {

    @Autowired
    private UserService userService;

    @ApiOperation(value = "createUser")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "userName", value = "USER_NAME", required = true, type = "String"),
        @ApiImplicitParam(name = "userPassword", value = "USER_PASSWORD", required = true, type = "String"),
        @ApiImplicitParam(name = "description", value = "DESCRIPTION", type = "String")
    })
    @PostMapping(value = "/create")
    @ResponseStatus(HttpStatus.CREATED)
    @ApiException(Status.CREATE_USER_ERROR)
    @AccessLogAnnotation(ignoreRequestArgs = {"loginUser", "userPassword"})
    public Result createUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                             @RequestParam(value = "userName") String userName,
                             @RequestParam(value = "userPassword") String userPassword,
                             @RequestParam(value = "description", required = false) String description) {
        Map<String, Object> result = userService.createUser(loginUser, userName, userPassword, description);
        return convertToResult(result);
    }

    @ApiOperation(value = "delUserById", notes = "DELETE_USER_BY_ID_NOTES")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "id", value = "user id", required = true, dataType = "Int", example = "1000")
    })
    @PostMapping(value = "/delete")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(Status.DELETE_USER_BY_ID_ERROR)
    @AccessLogAnnotation
    public Result deleteUserById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                                 @RequestParam(value = "userId") String userId) {
        Map<String, Object> result = userService.deleteUserByUserId(loginUser, userId);
        return convertToResult(result);
    }

    /**
     * user list no paging
     *
     * @param loginUser login user
     * @return user list
     */
    @ApiOperation(value = "listUser", notes = "LIST_USER_NOTES")
    @GetMapping(value = "/list")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(USER_LIST_ERROR)
    @AccessLogAnnotation
    public Result listUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser) {
        Map<String, Object> result = userService.queryAllGeneralUser(loginUser);
        return convertToResult(result);
    }

    /**
     * user list no paging
     *
     * @param loginUser login user
     * @return user list
     */
    @ApiOperation(value = "listAll", notes = "LIST_USER_NOTES")
    @GetMapping(value = "/list-all")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(USER_LIST_ERROR)
    @AccessLogAnnotation
    public Result listAll(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser) {
        Map<String, Object> result = userService.queryUserList(loginUser);
        return convertToResult(result);
    }

    /**
     * query user list paging
     *
     * @param loginUser login user
     * @param pageNo    page number
     * @param searchVal search avlue
     * @param pageSize  page size
     * @return user list page
     */
    @ApiOperation(value = "queryUserList", notes = "QUERY_USER_LIST_NOTES")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "pageNo", value = "PAGE_NO", required = true, dataType = "Int", example = "1"),
        @ApiImplicitParam(name = "pageSize", value = "PAGE_SIZE", required = true, dataType = "Int", example = "10"),
        @ApiImplicitParam(name = "searchVal", value = "SEARCH_VAL", type = "String")
    })
    @GetMapping(value = "/list-paging")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(QUERY_USER_LIST_PAGING_ERROR)
    @AccessLogAnnotation(ignoreRequestArgs = "loginUser")
    public Result queryUserList(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                                @RequestParam("pageNo") Integer pageNo,
                                @RequestParam("pageSize") Integer pageSize,
                                @RequestParam(value = "searchVal", required = false) String searchVal) {
        Result result = checkPageParams(pageNo, pageSize);
        if (!result.checkResult()) {
            return result;

        }
        result = userService.queryUserList(loginUser, searchVal, pageNo, pageSize);
        return result;
    }

    /**
     * update user
     *
     * @param loginUser   login user
     * @param userId      user id
     * @param description description
     * @return update result code
     */
    @ApiOperation(value = "updateUser", notes = "UPDATE_USER_NOTES")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "id", value = "USER_ID", required = true, dataType = "Int", example = "100"),
        @ApiImplicitParam(name = "description", value = "DESCRIPTION", required = true, type = "String")
    })
    @PostMapping(value = "/update")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(UPDATE_USER_ERROR)
    @AccessLogAnnotation(ignoreRequestArgs = "loginUser")
    public Result updateUser(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                             @RequestParam(value = "userId") String userId,
                             @RequestParam(value = "description", required = false) String description) {
        Map<String, Object> result = userService.updateUser(loginUser, userId, description);
        return convertToResult(result);
    }

    /**
     * verify username
     *
     * @param loginUser login user
     * @param userName  user name
     * @return true if user name not exists, otherwise return false
     */
    @ApiOperation(value = "verifyUserName", notes = "VERIFY_USER_NAME_NOTES")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "userName", value = "USER_NAME", required = true, type = "String"),
        @ApiImplicitParam(name = "id", value = "USER_ID", required = false, dataType = "int", example = "100")
    })
    @GetMapping(value = "/verify-user-name")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(VERIFY_USERNAME_ERROR)
    @AccessLogAnnotation
    public Result verifyUserName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                                 @RequestParam(value = "userName") String userName,
                                 @RequestParam(value = "userId", required = false) String userId) {
        return userService.verifyUserName(userName, userId);
    }

    /**
     * get user info
     *
     * @param loginUser login user
     * @return user info
     */
    @ApiOperation(value = "getUserInfo", notes = "GET_USER_INFO_NOTES")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "id", value = "USER_ID", required = true, dataType = "Int", example = "100")
    })
    @GetMapping(value = "/get-user-info")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(GET_USER_INFO_ERROR)
    @AccessLogAnnotation
    public Result getUserInfo(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                              @RequestParam(value = "userId") String userId) {
        Map<String, Object> result = userService.getUserInfo(loginUser, userId);
        return convertToResult(result);
    }

    /**
     * batch delete by ids
     *
     * @param loginUser login user
     * @param ids       user id list
     * @return delete result code
     */
    @ApiOperation(value = "batchDeleteByIds", notes = "BATCH_DELETE_USER_INSTANCE_BY_IDS_ERROR")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "ids", value = "USER_INSTANCE_IDS", required = true, dataType = "String")
    })
    @PostMapping(value = "/batch-delete")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(BATCH_DELETE_USER_INSTANCE_BY_IDS_ERROR)
    @AccessLogAnnotation(ignoreRequestArgs = "loginUser")
    public Result batchDeleteUserInstanceByIds(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                                               @RequestParam("ids") String ids) {
        Map<String, Object> result = new HashMap<>();
        List<String> deleteFailedIdList = new ArrayList<>();
        if (!StringUtils.isEmpty(ids)) {
            String[] userInstanceIdArray = ids.split(",");
            for (String userInstanceId : userInstanceIdArray) {
                try {
                    Map<String, Object> deleteResult = userService.deleteUserByUserId(loginUser, userInstanceId);
                    if (!Status.SUCCESS.equals(deleteResult.get(Constants.STATUS))) {
                        deleteFailedIdList.add(userInstanceId);
                        log.error("delete {} fail,due to: {}", userInstanceId, deleteResult.get(Constants.MESSAGE));
                    }
                } catch (Exception e) {
                    log.error("delete {} fail,due to: {}", userInstanceId, e);
                    deleteFailedIdList.add(userInstanceId);
                }
            }
        }

        if (!deleteFailedIdList.isEmpty()) {
            putMessage(result, BATCH_DELETE_USER_INSTANCE_BY_IDS_ERROR, String.join(",", deleteFailedIdList));
        } else {
            putMessage(result, Status.SUCCESS);
        }
        return convertToResult(result);
    }

    /**
     * reset password by id
     *
     * @param loginUser    login user
     * @param userId       user id
     * @param userPassword user password
     * @return reset result code
     */
    @ApiOperation(value = "resetPasswordById", notes = "RESET_PASSWORD_BY_ID")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "id", value = "USER_ID", required = true, dataType = "Int", example = "1000"),
        @ApiImplicitParam(name = "userPassword", value = "USER_PASSWORD", required = true, type = "String")
    })
    @PostMapping(value = "/reset-password")
    @ResponseStatus(HttpStatus.OK)
    @ApiException(Status.DELETE_USER_BY_ID_ERROR)
    @AccessLogAnnotation
    public Result resetPasswordById(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) UserEntity loginUser,
                                    @RequestParam(value = "userId") String userId,
                                    @RequestParam(value = "userPassword") String userPassword) {
        Map<String, Object> result = userService.resetPasswordById(loginUser, userId, userPassword);
        return convertToResult(result);
    }
}
